package cn.tedu.charging.order.server;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;

//定义websocket客户端连接的一个具体的endpoint 定义连接地址
//需要连接的时候 管理session 单独管理session需要管理sessionId 但是我们
//可以将每个客户端连接的session和userId进行绑定 从而双方进行通信的时候 只需要知道userId是谁就ok了
@Slf4j
@Component
@ServerEndpoint("/ws/server/{userId}")
public class WebSocketServer {
    /**
     * 用户通过ServerEndpoint连接当前服务端
     * 连接可以通过以下方法建立,同时初始化当前会话和userId绑定关系
     * 开始建立连接的时候 会调用@OnOpen方法
     * session: 客户端和服务端建立通信后的会话连接
     * userId: 我们自定义的客户端建立连接的时候传递userId
     */
    private static HashMap<Integer,Session> REGISTER=new HashMap<>();
    //OnOpen
    @OnOpen
    public void onOpen(Session session,@PathParam("userId")Integer userId){
        //每当有一个客户端通过当前serverEndpoint建立连接的时候,连接会话session就是方法入参.
        //websocket中的大量session的管理工作 ,既要存储,也要记录和客户端映射 可以选择业务数据和session做绑定
        //通过userId这个业务数据 建立和session映射 使用map存储数据
        log.info("用户:{}开始建立连接",userId);
        REGISTER.put(userId,session);
        log.info("用户:{},连接:{},建立成功",userId,session.getId());
    }
    //连接session关闭的时候会调用OnClose方法 清理工作 移除userId-session关系 客户端会向当前断点发送关闭请求
    //OnClose
    @OnClose
    public void onClose(Session session,@PathParam("userId")Integer userId){
        //每当有一个客户端通过当前serverEndpoint建立连接的时候,连接会话session就是方法入参.
        //websocket中的大量session的管理工作 ,既要存储,也要记录和客户端映射 可以选择业务数据和session做绑定
        //通过userId这个业务数据 建立和session映射 使用map存储数据
        log.info("用户:{}开始关闭连接",userId);
        REGISTER.remove(userId);
        log.info("用户:{},连接:{},关闭成功",userId,session.getId());
    }
    //OnError
    //如果我们建立连接出现了问题 异常
    @OnError
    public void onError(Session session,Throwable throwable){
        log.error("连接出现异常",throwable);
    }
    //OnMessage
    //每次客户端和服务端之间有数据交互 都会调用这个方法

    /**
     * @param message 双方传递的消息内容
     * @param session 双方通信使用的session对象
     */
    @OnMessage
    public void onMessage(String message,Session session){
        log.info("开始通信消息");
        log.info("会话:{},发送消息:{}",session.getId(),message);
    }
    //提供一个服务端的方法,可以允许程序 向客户端发送消息
    /**
     * @param userId 业务中给客户端发送消息 需要提供一个业务数据userId 从而能够在注册器里找到客户端连接
     * @param message 服务端要给客户端发送的信息内容
     * @return Boolean 告知调用业务发送消息给客户端成功还是失败
     */
    public Boolean pushMessage(String message,Integer userId){
        //1.z在业务中能够获取userId,从而在终端映射一个客户端session会话
        Session session = REGISTER.get(userId);
        //如果客户端断开连接 会话关闭 拿到的就是空
        if (session!=null){
            //2.说明当前userId映射的客户端连接没有断开可以用
            try{
                log.info("当前用户:{},使用session:{},推送消息:{}",userId,session.getId(),message);
                session.getBasicRemote().sendText(message);
                return true;
            }catch (Exception e){
                log.error("发送消息失败",e);
            }
        }else{
            log.error("用户:{},连接断开",userId);
        }
        return false;
    }
}
